package data.base;

import data.hierarchy.IVertexProvider;

public class Transform {
  private int theMatrix = 0;
  private int tempMatrix = 1;
  private int transMatrix = 2;
  private double[][][] matrices = new double[3][4][4];
  
  public Transform(){
    matrices[theMatrix] = getIdentityMatrix();
  }
  
  public void applyToPoint(Vector3 p){
    double x = p.getX();
    double y = p.getY();
    double z = p.getZ();
    
    p.setX(matrices[theMatrix][0][0] * x + matrices[theMatrix][1][0] * y + matrices[theMatrix][2][0] * z + matrices[theMatrix][3][0]);
    p.setY(matrices[theMatrix][0][1] * x + matrices[theMatrix][1][1] * y + matrices[theMatrix][2][1] * z + matrices[theMatrix][3][1]);
    p.setZ(matrices[theMatrix][0][2] * x + matrices[theMatrix][1][2] * y + matrices[theMatrix][2][2] * z + matrices[theMatrix][3][2]);
  }
  
  public void applyTo(IVertexProvider pointsProvider){
    for(Vertex v : pointsProvider.getVertices())
      applyToPoint(v);  
  }
  
  public void translateBy(double x, double y, double z){
    matrices[transMatrix] = getIdentityMatrix();
    matrices[transMatrix][3][0] = x;
    matrices[transMatrix][3][1] = y;
    matrices[transMatrix][3][2] = z;
    applyTransformation();
  }
  
  public void scaleBy(double x, double y, double z){
    matrices[transMatrix] = new double[4][4];
    matrices[transMatrix][0][0] = x;
    matrices[transMatrix][1][1] = y;
    matrices[transMatrix][2][2] = z;
    matrices[transMatrix][3][3] = 1;
    applyTransformation();
  }
  
  public void rotateAroundX(double angle){
    matrices[transMatrix] = getIdentityMatrix();
    matrices[transMatrix][1][1] = Math.cos(angle);
    matrices[transMatrix][1][2] = Math.sin(angle);
    matrices[transMatrix][2][1] = -matrices[transMatrix][1][2];
    matrices[transMatrix][2][2] = matrices[transMatrix][1][1];
    applyTransformation();
  }
  
  public void rotateAroundY(double angle){
    matrices[transMatrix] = getIdentityMatrix();
    matrices[transMatrix][0][0] = Math.cos(angle);
    matrices[transMatrix][2][0] = Math.sin(angle);
    matrices[transMatrix][0][2] = -matrices[transMatrix][2][0];
    matrices[transMatrix][2][2] = matrices[transMatrix][0][0];
    applyTransformation();
  }
  
  public void rotateAroundZ(double angle){
    matrices[transMatrix] = getIdentityMatrix();
    matrices[transMatrix][0][0] = Math.cos(angle);
    matrices[transMatrix][1][0] = Math.sin(angle);
    matrices[transMatrix][0][1] = -matrices[transMatrix][1][0];
    matrices[transMatrix][1][1] = matrices[transMatrix][0][0];
    applyTransformation();
  }
  
  /*//Doesn't work yet
  public void rotateAroundVector(double angle, Vector3 axis){
    double u = axis.getX();
    double v = axis.getY();
    double w = -axis.getZ();
    double u2 = u * u;
    double v2 = v * v;
    double w2 = w * w;
    double uvw2 = u2 + v2 + w2;
    double sin = Math.sin(angle);
    double cos = Math.cos(angle);
    
    matrices[transMatrix] = getIdentityMatrix();
    matrices[transMatrix][0][0] = (u2 + (v2 + w2) * cos) / uvw2;
    matrices[transMatrix][0][1] = (u * v * (1 - cos) + w * Math.sqrt(uvw2) * sin) / uvw2;
    matrices[transMatrix][0][2] = (u * w * (1 - cos) - v * Math.sqrt(uvw2) * sin) / uvw2;
    
    matrices[transMatrix][1][0] = (u * v * (1 - cos) - w * Math.sqrt(uvw2) * sin) / uvw2;
    matrices[transMatrix][1][1] = (v2 + (u2 + w2) * cos) / uvw2;
    matrices[transMatrix][1][2] = (v * w * (1 - cos) + u * Math.sqrt(uvw2) * sin) / uvw2;
    
    matrices[transMatrix][2][0] = (u * w * (1 - cos) + v * Math.sqrt(uvw2) * sin) / uvw2;
    matrices[transMatrix][2][1] = (v * w * (1 - cos) - u * Math.sqrt(uvw2) * sin) / uvw2;
    matrices[transMatrix][2][2] = (w2 + (u2 + v2) * cos) / uvw2;
    applyTransformation();
  }
  */
  
  private void applyTransformation(){
    switchMatrixFocus();
    
    for(int i = 0; i < 4; i++){
      for(int j = 0; j < 4; j++){
        matrices[theMatrix][i][j] = 
          matrices[tempMatrix][i][0] * matrices[transMatrix][0][j] +
          matrices[tempMatrix][i][1] * matrices[transMatrix][1][j] +
          matrices[tempMatrix][i][2] * matrices[transMatrix][2][j] +
          matrices[tempMatrix][i][3] * matrices[transMatrix][3][j];
      }        
    }
  }
  
  private void switchMatrixFocus(){
    if(theMatrix == 0){
      tempMatrix = 0;
      theMatrix = 1;
    }
    else{
      tempMatrix = 1;
      theMatrix = 0;
    }
  }
  
  private  double[][] getIdentityMatrix(){
    double[][] identity = new double[4][4];
    identity[0][0] = 1;
    identity[1][1] = 1;
    identity[2][2] = 1;
    identity[3][3] = 1;
    return identity;
  }
}
